package cn.edu.jsu.zjj.model.utils;

import cn.edu.jsu.zjj.model.entity.Docu;
import cn.edu.jsu.zjj.model.entity.SysData;
import cn.edu.jsu.zjj.model.entity.SysHeader;
import cn.edu.jsu.zjj.model.vo.ZipVo;
import cn.edu.jsu.zjj.service.DocuService;
import cn.edu.jsu.zjj.service.ISysDataService;
import cn.edu.jsu.zjj.service.ISysHeaderService;
import cn.edu.jsu.zjj.service.UserService;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import kotlin.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class ZipUtils {
    // 创建一个工作簿对象，并返回该对象
    /**
     * 创建包含数据的Excel工作簿。
     * @return 返回一个包含数据的工作簿对象，该工作簿中有一个名为"Sheet1"的工作表，工作表中的数据格式为"Cell i,j"，其中i和j分别代表行号和列号。
     */


    /**
     * 创建包含数据的Excel工作簿。
     * @param headers 表头列表
     * @param data 数据列表
     * @return 返回一个包含数据的工作簿对象，该工作簿中有一个名为"Sheet1"的工作表，
     *         工作表中的第一行为表头，后续行为数据。
     */
    // 该方法用于创建一个Excel工作簿，包含表头和数据行。

    public static Workbook createWorkbookWithData(List<String> headers, List<List<Object>> data,String excelName) {
        // 创建一个新的XSSFWorkbook对象，代表一个Excel工作簿
        Workbook workbook = new XSSFWorkbook();
        // 在工作簿中创建一个名为"Sheet1"的工作表
        Sheet sheet = workbook.createSheet("Sheet1");

        // 创建一个CellStyle对象，用于设置字体颜色为红色
        CellStyle headerStyle = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true); // 设置字体为加粗
        headerStyle.setFont(font);

        // 设置字体居中
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中

        // 设置边框样式为加粗
        headerStyle.setBorderTop(BorderStyle.MEDIUM);
        headerStyle.setBorderBottom(BorderStyle.MEDIUM);
        headerStyle.setBorderLeft(BorderStyle.MEDIUM);
        headerStyle.setBorderRight(BorderStyle.MEDIUM);

        // 创建第一行
        Row firstRow = sheet.createRow(0);
        // 创建一个单元格以确保合并区域中的单元格存在
        Cell mergedCell = firstRow.createCell(0);
        mergedCell.setCellValue(excelName);
        mergedCell.setCellStyle(headerStyle);
        // 定义合并区域，从第一行第一列到第一行最后一列
        CellRangeAddress cellRangeAddress = new CellRangeAddress(0,0,0,0);
        if (headers.size() > 1) {
             cellRangeAddress = new CellRangeAddress(0, 0, 0, headers.size() - 1);
            sheet.addMergedRegion(cellRangeAddress);
        }

        firstRow.setHeightInPoints(40); // 设置第一行高度为40点

        // 创建字体样式
        Font font1 = workbook.createFont();
        font1.setFontHeightInPoints((short) 24); // 设置字体大小为20磅
        // 创建边框,居中样式
        CellStyle borderStyle = workbook.createCellStyle();
        borderStyle.setBorderTop(BorderStyle.THICK);
        borderStyle.setBorderBottom(BorderStyle.THICK);
        borderStyle.setBorderLeft(BorderStyle.THICK);
        borderStyle.setBorderRight(BorderStyle.THICK);
        borderStyle.setAlignment(HorizontalAlignment.CENTER);
        borderStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
        font1.setBold(true); // 设置字体为加粗
        borderStyle.setFont(font1);

        // 应用边框样式到合并的区域
        for (int i = cellRangeAddress.getFirstRow(); i <= cellRangeAddress.getLastRow(); i++) {
            for (int j = cellRangeAddress.getFirstColumn(); j <= cellRangeAddress.getLastColumn(); j++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    row = sheet.createRow(i);
                }
                Cell cell = row.getCell(j);
                if (cell == null) {
                    cell = row.createCell(j);
                }
                cell.setCellStyle(borderStyle);
            }
        }

        // 创建表头行
        Row headerRow = sheet.createRow(1);
        headerRow.setHeightInPoints(25); // 设置表头行高度为20点
        for (int j = 0; j < headers.size(); j++) {
            Cell headerCell = headerRow.createCell(j);
            headerCell.setCellValue(headers.get(j));
            headerCell.setCellStyle(headerStyle);
        }

        // 初始化列宽列表
        List<Integer> columnWidths = new ArrayList<>();
        int defaultWidth = 20;
        for (int i = 0; i < headers.size(); i++) {
            columnWidths.add(defaultWidth);
        }

        // 创建数据行
        for (int i = 0; i < data.size(); i++) {
            Row dataRow = sheet.createRow(i + 2); // 从第四行开始，因为前三行分别是合并单元格、空行和表头
            dataRow.setHeightInPoints(20); // 设置数据行高度为15点
            List<Object> rowData = data.get(i);

            // 创建一个居中对齐的单元格样式
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中

            // 设置边框样式为加粗
            cellStyle.setBorderTop(BorderStyle.MEDIUM);
            cellStyle.setBorderBottom(BorderStyle.MEDIUM);
            cellStyle.setBorderLeft(BorderStyle.MEDIUM);
            cellStyle.setBorderRight(BorderStyle.MEDIUM);

            for (int j = 0; j < rowData.size(); j++) {
                Cell dataCell = dataRow.createCell(j);
                Object cellValue = rowData.get(j);
                dataCell.setCellStyle(cellStyle);
            if (cellValue != null) {
                if (cellValue instanceof String) {
                    dataCell.setCellValue((String) cellValue);
                    int cellLength = ((String) cellValue).length();
                    columnWidths.set(j, Math.max(columnWidths.get(j), cellLength));
                } else if (cellValue instanceof Integer) {
                    dataCell.setCellValue((Integer) cellValue);
                    int cellLength = String.valueOf(cellValue).length();
                    columnWidths.set(j, Math.max(columnWidths.get(j), cellLength));
                } else if (cellValue instanceof Double) {
                    dataCell.setCellValue((Double) cellValue);
                    int cellLength = String.valueOf(cellValue).length();
                    columnWidths.set(j, Math.max(columnWidths.get(j), cellLength));
                } else if (cellValue instanceof Boolean) {
                    dataCell.setCellValue((Boolean) cellValue);
                    int cellLength = String.valueOf(cellValue).length();
                    columnWidths.set(j, Math.max(columnWidths.get(j), cellLength));
                } else if (cellValue != null) {
                    dataCell.setCellValue(cellValue.toString());
                    int cellLength = cellValue.toString().length();
                    columnWidths.set(j, Math.max(columnWidths.get(j), cellLength));
                } else {
                    columnWidths.set(j, Math.max(columnWidths.get(j), 0));
                }
            } else {
                // 处理 cellValue 为 null 的情况
                dataCell.setCellValue("");
                columnWidths.set(j, Math.max(columnWidths.get(j), 0));
            }

            }
        }

        // 调整列宽
        for (int i = 0; i < columnWidths.size(); i++) {
            sheet.setColumnWidth(i, columnWidths.get(i) * 256+256*8); // 将字符长度转换为像素
        }

        return workbook;
    }








    /**
     * 主函数，用于创建工作簿对象，填充数据，并将其压缩后发送给客户端。
     *
     * @throws IOException 如果发生I/O错误，则抛出此异常。
     */
    public static void main(HttpServletResponse response, List<String> headers, List<List<Object>> data, Pair<List<String>, List<byte[]>> pair,String excelName,List<List<Object>>removeList) throws IOException {
        // 创建一个工作簿对象，并填充数据
        // 创建工作簿
        Workbook workbook = createWorkbookWithData(headers, data,excelName);
        //TODO:附件目录
        String directoryPath = excelName + "-附件/";
        // 创建一个字节数组输出流，用于存储压缩后的数据
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 创建一个ZIP输出流，用于将文件或工作簿数据写入到字节数组输出流中
        ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);

        // 获取工作簿对象的字节数组表示形式
        byte[] workbookBytes = getWorkbookBytes(workbook);
        // 创建一个ZIP条目，表示将要添加到ZIP文件中的工作簿
        ZipEntry workbookEntry = new ZipEntry(excelName + ".xlsx");
        // 将ZIP条目添加到ZIP输出流中
        zipOutputStream.putNextEntry(workbookEntry);
        // 将工作簿的字节数组写入到ZIP输出流中
        zipOutputStream.write(workbookBytes);
        // 关闭当前ZIP条目
        zipOutputStream.closeEntry();

        // 添加附件目录
        String attachmentDirectory = excelName + "-附件/";
        ZipEntry directoryPathEntry = new ZipEntry(attachmentDirectory);
        zipOutputStream.putNextEntry(directoryPathEntry);
        zipOutputStream.closeEntry();

        for (int i = 0; i < pair.getFirst().size(); i++) {
            byte[] binaryArray = pair.getSecond().get(i);
            // 添加二进制数组到ZIP压缩包中
            String binaryEntryName = pair.getFirst().get(i); // 您想要在ZIP文件中使用的名称
            ZipEntry binaryEntry = new ZipEntry(attachmentDirectory + binaryEntryName);
            zipOutputStream.putNextEntry(binaryEntry);
            zipOutputStream.write(binaryArray);
            zipOutputStream.closeEntry();
        }
        zipOutputStream.closeEntry();
        // 关闭ZIP输出流
        zipOutputStream.close();
        // 将压缩后的数据发送给客户端
        sendZipToClient(byteArrayOutputStream.toByteArray(), response,excelName);
    }


    public static void totalMain(HttpServletResponse response, List<ZipVo> zipVos) throws IOException {
        // 创建一个字节数组输出流，用于存储压缩后的数据
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 创建一个ZIP输出流，用于将文件或工作簿数据写入到字节数组输出流中
        ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);

        // 添加文件总体目录
        String totalZipDirectory = "选中的下载文件/";
        ZipEntry totalDirectoryEntry = new ZipEntry(totalZipDirectory);
        zipOutputStream.putNextEntry(totalDirectoryEntry);
        zipOutputStream.closeEntry();

        for (ZipVo zipVo : zipVos) {
            // 创建一个工作簿对象，并填充数据
            Workbook workbook = createWorkbookWithData(zipVo.getHeaders(), zipVo.getData(), zipVo.getExcelName());

            // 获取工作簿对象的字节数组表示形式
            byte[] workbookBytes = getWorkbookBytes(workbook);

            // 创建文件目录
            String totalFileDirectory = zipVo.getExcelName() + "-" + System.currentTimeMillis() + "/";
            String attachmentDirectory = zipVo.getExcelName() + "-附件/";

            // 创建文件目录条目
            ZipEntry fileDirectoryEntry = new ZipEntry(totalZipDirectory + totalFileDirectory);
            zipOutputStream.putNextEntry(fileDirectoryEntry);
            zipOutputStream.closeEntry();

            // 创建附件目录条目
            ZipEntry attachmentDirectoryEntry = new ZipEntry(totalZipDirectory + totalFileDirectory + attachmentDirectory);
            zipOutputStream.putNextEntry(attachmentDirectoryEntry);
            zipOutputStream.closeEntry();

            // 创建工作簿条目
            ZipEntry workbookEntry = new ZipEntry(totalZipDirectory + totalFileDirectory + zipVo.getExcelName() + ".xlsx");
            zipOutputStream.putNextEntry(workbookEntry);
            zipOutputStream.write(workbookBytes);
            zipOutputStream.closeEntry();

            // 添加附件文件
            for (int i = 0; i < zipVo.getPair().getFirst().size(); i++) {
                byte[] binaryArray = zipVo.getPair().getSecond().get(i);
                String binaryEntryName = zipVo.getPair().getFirst().get(i);
                ZipEntry binaryEntry = new ZipEntry(totalZipDirectory + totalFileDirectory + attachmentDirectory + binaryEntryName);
                zipOutputStream.putNextEntry(binaryEntry);
                zipOutputStream.write(binaryArray);
                zipOutputStream.closeEntry();
            }
        }

        // 关闭ZIP输出流
        zipOutputStream.close();

        // 将压缩后的数据发送给客户端
        sendZipToClient(byteArrayOutputStream.toByteArray(), response, "response");
    }

    /**
     * 将工作簿对象转换为字节数组。
     *
     * @param workbook 需要转换的工作簿对象
     * @return 返回工作簿对象的字节数组表示形式
     * @throws IOException 如果写入字节流时发生错误，则抛出此异常
     */
    private static byte[] getWorkbookBytes(Workbook workbook) throws IOException {
        // 创建一个字节数组输出流，用于存储工作簿的字节数据
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // 将工作簿的内容写入到字节数组输出流中
        workbook.write(byteArrayOutputStream);
        // 将字节数组输出流转换为字节数组并返回
        return byteArrayOutputStream.toByteArray();

    }

    // 定义一个私有的静态方法，用于将压缩文件发送到客户端
    /**
     * 将压缩数据发送到客户端。
     * @param zipData 需要发送的压缩数据。
     * @throws IOException 如果发生输入/输出错误，则抛出此异常。
     */
    private static void sendZipToClient(byte[] zipData, HttpServletResponse response, String fileName) throws IOException {
        fileName = "response";
        String encodedFileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        response.setContentType("application/zip"); // 设置响应内容类型为ZIP格式
        // 对于支持RFC 5987的浏览器
        if (isRFC5987Supported(response)) {
            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(fileName+".zip", "UTF-8"));
        } else {
            // 对于不支持RFC 5987的老版本浏览器（如IE），提供备选方案
            response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName + ".zip");
        }
        OutputStream outputStream = response.getOutputStream(); // 获取响应的输出流
        log.info("sendZipToClient: " + fileName + ".zip");
        outputStream.write(zipData); // 将压缩数据写入输出流
        outputStream.flush(); // 刷新输出流，确保所有数据都被写出
        outputStream.close(); // 关闭输出流
    }

    public static boolean isRFC5987Supported(HttpServletResponse response) {
        String userAgent = response.getHeader("User-Agent");
        return userAgent != null && !userAgent.contains("MSIE");
    }



    public static byte[] urlBytes = null;

    public static String fileName = "";
    public static void urlToByte(String urls) {
        if (StringUtils.isEmpty(urls)) {
            urlBytes = null;
        }
        try {
            fileName = "";
            urlBytes = null;
            // 创建URL对象
            URL url = new URL(urls);

            fileName = urls.substring(urls.lastIndexOf("/") + 1);
            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // 设置用户代理
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3");

            // 获取输入流
            InputStream inputStream = connection.getInputStream();

            // 创建字节数组输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            // 读取输入流并写入字节数组输出流
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }

            // 关闭输入流和输出流
            inputStream.close();
            byteArrayOutputStream.close();

            // 将字节数组输出流转换为字节数组
            urlBytes = byteArrayOutputStream.toByteArray();

            // 处理二进制数据（例如保存到文件或发送到其他服务）
            // ...

        } catch (IOException e) {
            if (e instanceof FileNotFoundException) {
                log.error("文件未找到: {}", urls);
            } else if (e.getMessage().contains("403")) {
                log.error("服务器返回 403 Forbidden 错误: {}", urls);
            } else {
                log.error("发生 IO 异常: {}", e.getMessage(), e);
            }
        }

    }
    private static final Map<String, String> CONTENT_TYPE_MAP = new HashMap<>();
    // 初始化文件类型和Content-Type的映射关系, 根据需要添加更多类型
    static {
        CONTENT_TYPE_MAP.put("pdf", "application/pdf");
        CONTENT_TYPE_MAP.put("doc", "application/msword");
        CONTENT_TYPE_MAP.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        CONTENT_TYPE_MAP.put("xls", "application/vnd.ms-excel");
        CONTENT_TYPE_MAP.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        CONTENT_TYPE_MAP.put("ppt", "application/vnd.ms-powerpoint");
        CONTENT_TYPE_MAP.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
        CONTENT_TYPE_MAP.put("txt", "text/plain");
        CONTENT_TYPE_MAP.put("csv", "text/csv");
        CONTENT_TYPE_MAP.put("zip", "application/zip");
        CONTENT_TYPE_MAP.put("jpg", "image/jpeg");
        CONTENT_TYPE_MAP.put("jpeg", "image/jpeg");
        CONTENT_TYPE_MAP.put("png", "image/png");
        CONTENT_TYPE_MAP.put("gif", "image/gif");
        CONTENT_TYPE_MAP.put("svg", "image/svg+xml");
        CONTENT_TYPE_MAP.put("mp3", "audio/mpeg");
        CONTENT_TYPE_MAP.put("mp4", "video/mp4");
        CONTENT_TYPE_MAP.put("avi", "video/x-msvideo");
        CONTENT_TYPE_MAP.put("mov", "video/quicktime");
        CONTENT_TYPE_MAP.put("html", "text/html");
        CONTENT_TYPE_MAP.put("css", "text/css");
        CONTENT_TYPE_MAP.put("js", "application/javascript");
        CONTENT_TYPE_MAP.put("json", "application/json");
        CONTENT_TYPE_MAP.put("xml", "application/xml");
        CONTENT_TYPE_MAP.put("mpeg", "video/mpeg");
        CONTENT_TYPE_MAP.put("mpg", "video/mpeg");
        CONTENT_TYPE_MAP.put("ogg", "audio/ogg");
        CONTENT_TYPE_MAP.put("wav", "audio/wav");
        CONTENT_TYPE_MAP.put("webm", "video/webm");
        CONTENT_TYPE_MAP.put("woff", "application/font-woff");
        CONTENT_TYPE_MAP.put("woff2", "application/font-woff2");
        CONTENT_TYPE_MAP.put("ttf", "application/font-sfnt");
        CONTENT_TYPE_MAP.put("eot", "application/vnd.ms-fontobject");
        CONTENT_TYPE_MAP.put("otf", "application/x-font-opentype");

    }

    public static void send(byte[] zipData, HttpServletResponse response, String fileName, String suffix) throws IOException {
        // 添加时间戳到文件名
        String newFileName = fileName + "-" + System.currentTimeMillis() + "." + suffix;
        // 编码文件名
        String encodedFileName = new String(newFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);

        // 设置响应内容类型,如果匹配到文件类型，则使用Content-Type，否则使用application/octet-stream
        String contentType = CONTENT_TYPE_MAP.getOrDefault(suffix.toLowerCase(), "application/octet-stream");
        response.setContentType(contentType);

//        response.setContentType("multipart/form-data");
        // 对于支持RFC 5987的浏览器
        if (isRFC5987Supported(response)) {
            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(newFileName, "UTF-8"));
        } else {
            // 对于不支持RFC 5987的老版本浏览器（如IE），提供备选方案
            response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
        }

        // 获取响应的输出流
        OutputStream outputStream = response.getOutputStream();

        // 记录日志
        log.info("sendFileToClient: " + newFileName);

        // 将文件数据写入输出流
        outputStream.write(zipData);

        // 刷新输出流，确保所有数据都被写出
        outputStream.flush();

        // 关闭输出流
        outputStream.close();
    }

}
